#ifndef _WIN32

#include "thread.h"
#include <pthread.h>

using namespace krryn::core;

namespace krryn{
namespace core_imp{    
	class event_handle{
    public:
		pthread_cond_t *m_Event;
        pthread_mutex_t *m_Mutex;
        
        event_handle(){
            m_Event = new pthread_cond_t();
            m_Mutex = new pthread_mutex_t();
            
            pthread_mutex_init(m_Mutex, NULL);
            pthread_cond_init(m_Event, NULL);
        }
        
        ~event_handle(){
            pthread_mutex_destroy(m_Mutex);
            pthread_cond_destroy(m_Event);
            
            delete m_Mutex, m_Mutex = 0;
            delete m_Event, m_Event = 0;
        }
	};
    
	class thread_handle{
    public:
		pthread_t m_Thread;
        
		thread_handle() : m_Thread(0) {}
        
		static void* callback(void *a_Parameter){
            ((thread*)a_Parameter)->run();
        }
    };
    
	// recursive mutices aren't implemented on all POSIX platforms
	// (old glibc versions might not support it) so we might have to
	// look into creating our own. (Which should be simple enough).
	// Example: http://wiki.forum.nokia.com/index.php/Recursive_mutex
	class mutex_handle{
    public:
		pthread_mutex_t *m_Mutex;
        
		mutex_handle(){
            m_Mutex = new pthread_mutex_t();
            
            pthread_mutex_init(m_Mutex, NULL);
			pthread_mutexattr_settype(m_Mutex, PTHREAD_MUTEX_RECURSIVE);
		}
        
		~mutex_handle(){
            pthread_mutex_destroy(m_Mutex);
            delete m_Mutex, m_Mutex = 0;
		}
	};
}
}

using namespace krryn::core_imp;

event::event(const std::string &a_Name) : m_Name(a_Name){
    m_Handle = new event_handle();
}

event::~event(){
    delete m_Handle, m_Handle = 0;
}

std::string event::name(){
	return m_Name;
}

void event::trigger() volatile{
    pthread_mutex_lock(m_Handle->m_Mutex);
    pthread_cond_broadcast(m_Handle->m_Event);
    pthread_mutex_unlock(m_Handle->m_Mutex);
}

bool event::wait() volatile{
    pthread_mutex_lock(m_Handle->m_Mutex);
    pthread_cond_wait(m_Handle->m_Event, m_Handle->m_Mutex);
    pthread_mutex_unlock(m_Handle->m_Mutex);
}

mutex::mutex(){
    m_Handle = new mutex_handle();
}

mutex::~mutex(){
    delete m_Handle, m_Handle  = 0;
}

void mutex::lock(){
    pthread_mutex_lock(m_Handle->m_Mutex);
}

void mutex::unlock(){
    pthread_mutex_unlock(m_Handle->m_Mutex);
}

thread::thread(){
    m_Handle = new thread_handle();
}

thread::~thread(){
    kill();
    
    delete m_Handle, m_Handle = 0;
}

void thread::start(){
    pthread_create(
        &(m_Handle->m_Thread), 
        NULL, 
        thread_handle::callback, 
        (void*)this
    );
}

void thread::end(){
    pthread_join(m_Handle->m_Thread, NULL);
}

void thread::sleep(long a_MilliSeconds){
    struct timespec l_Request = {0}, l_Remaining = {0};
    time_t l_Seconds = (int)a_MilliSeconds / 1000;
    a_MilliSeconds -= l_Seconds * 1000;
    l_Request.tv_sec = l_Seconds;
    l_Request.tv_nsec = a_MilliSeconds * 1000000L;
    nanosleep(&l_Request, &l_Remaining);
}

void thread::kill(){
    pthread_cancel(m_Handle->m_Thread);
}

wait_all::wait_all(thread **a_Thread, int a_Amount){
    for(int i = 0; i < a_Amount; i++){
        pthread_join(a_Thread[i]->m_Handle->m_Thread, NULL);
        a_Thread[i]->end();
    }
}

#endif
